package com.gxar.fusion.authorization.interception;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson2.JSON;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.gxar.common.entity.Result;
import com.gxar.common.entity.SystemCodeEnum;
import com.gxar.common.entity.login.LoginUserThread;
import com.gxar.common.entity.login.UserProfile;
import com.gxar.common.utils.JwtUtils;
import com.gxar.fusion.authorization.constant.JwtConstant;
import com.gxar.fusion.authorization.constant.UserConstant;
import com.gxar.fusion.authorization.model.ResultCodeEnum;
import com.gxar.fusion.authorization.properties.AuthProperties;
import com.gxar.fusion.authorization.properties.LoginProperties;
import com.gxar.fusion.authorization.properties.SysProperties;
import com.gxar.fusion.authorization.reference.UserReference;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.NotNull;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.*;

/**
 * 登录拦截器
 * 校验用户登录状态,将用户信息放入线程对象
 *
 * @author Linsy
 * @version 1.0
 * @created 2023/10/17
 */
@Component
@Slf4j
public class LoginInterception implements HandlerInterceptor {

    @Resource
    private UserReference userReference;
    @Resource
    private AuthProperties authProperties;

    @Override
    public boolean preHandle(HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull Object handler) throws Exception {
        LoginProperties loginProperties = authProperties.getLogin();
        if (Objects.nonNull(loginProperties)) {
            List<String> noLoginUrl = loginProperties.getIgnoreUrl();
            // 登录白名单直接放行
            if (Optional.ofNullable(noLoginUrl).orElse(Collections.emptyList())
                    .stream().anyMatch(e -> request.getRequestURI().startsWith(e))) {
                return true;
            }
        }
        // 获取token
        String authorization = request.getHeader("Authorization");
        if (StringUtils.isNotBlank(authorization)) {
            return jwtAuth(response, authorization);
        } else {
            return userLogin(request, response);
        }
    }

    /**
     * 返回未登录的错误信息
     *
     * @param response ServletResponse
     */
    private void returnNoLogin(HttpServletResponse response) throws IOException {
        try (ServletOutputStream outputStream = response.getOutputStream()) {
            // 设置返回401 和响应编码
            response.setStatus(401);
            response.setContentType("Application/json;charset=utf-8");
            // 构造返回响应体
            Result<Object> result = Result.error(SystemCodeEnum.UN_AUTH);
            String resultString = JSON.toJSONString(result);
            outputStream.write(resultString.getBytes(StandardCharsets.UTF_8));
        }
    }

    /**
     * preHandle 返回true时清理对象
     *
     * @param request  请求
     * @param response 响应
     * @param handler  handler
     * @param ex       异常
     */
    @Override
    public void afterCompletion(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull Object handler, @Nullable Exception ex) {
        // 释放用户线程对象
        LoginUserThread.remove();
    }

    /**
     * 用户登录校验
     *
     * @param request  请求
     * @param response 响应
     * @return 是否放行
     * @throws IOException 异常
     */
    private boolean userLogin(HttpServletRequest request, HttpServletResponse response) throws IOException {
        String token = request.getHeader(UserConstant.TOKEN_HEADER_NAME);
        String userId = request.getHeader(UserConstant.USER_ID);
        String app = request.getHeader(UserConstant.APP_NAME);
        String platform = request.getHeader(UserConstant.PLATFORM);
        if (StringUtils.isBlank(token) || StringUtils.isBlank(userId)) {
            returnNoLogin(response);
            return false;
        }
        // 对登录信息进行校验
        JSONObject resultObject = userReference.checkSessionService(userId, token, app, platform);
        Object data = resultObject.get("data");
        if (Objects.isNull(data)) {
            log.error("用户未登陆,查询登录状态data为空");
            returnNoLogin(response);
            return false;
        }
        JSONObject jsonObject = com.alibaba.fastjson.JSON.parseObject(com.alibaba.fastjson.JSON.toJSONString(data));
        Boolean isLogin = jsonObject.getBoolean("is_login");
        if (!isLogin) {
            try {
                Long.parseLong(userId);
            } catch (Exception e) {
                // 只对异常数据做log预警
                log.error("用户未登陆,is_login不为true。user-id:[{}], token:[{}]，url:[{}]，用户中心响应：[{}]", userId, token, request.getRequestURI(), com.alibaba.fastjson.JSON.toJSONString(jsonObject));
            }
            returnNoLogin(response);
            return false;
        }
        UserProfile userProfile = new UserProfile();
        userProfile.setId(Long.parseLong(userId));
        userProfile.setToken(token);
        if (StringUtils.isBlank(app)) {
            app = request.getParameter(UserConstant.APP_NAME);
        }
        SysProperties sys = authProperties.getSys();
        if (StringUtils.isBlank(app) && Objects.nonNull(sys)) {
            app = sys.getApp();
        }
        if (StringUtils.isBlank(platform)) {
            platform = request.getParameter(UserConstant.PLATFORM);
        }
        if (StringUtils.isBlank(platform) && Objects.nonNull(sys)) {
            platform = sys.getPlatform();
        }
        userProfile.setApp(app);
        userProfile.setPlatform(platform);
        // 用户已登录,将用户信息放入线程对象
        JSONObject profileJsonObject = userReference.profileService(userId, token, app, platform);
        Object profileData = profileJsonObject.get("data");
        if (Objects.nonNull(profileData)) {
            JSONObject profileDateObject = com.alibaba.fastjson.JSON.parseObject(com.alibaba.fastjson.JSON.toJSONString(profileData));
            Object profileObject = profileDateObject.get("profile");
            if (Objects.nonNull(profileObject)) {
                JSONObject profile = com.alibaba.fastjson.JSON.parseObject(com.alibaba.fastjson.JSON.toJSONString(profileObject));
                userProfile.setEmail(profile.getString("email"));
                userProfile.setUsername(profile.getString("username"));
                userProfile.setNickname(profile.getString("nickname"));
                userProfile.setRegTime(profile.getLong("reg_time"));
                userProfile.setAvatar(profile.getString("avatar"));
                userProfile.setMobile(profile.getString("mobile"));
                userProfile.setSex(profile.getInteger("sex"));
                userProfile.setExtra(profile.getString("extra"));
            }
        }
        // 存放用户线程对象
        LoginUserThread.put(userProfile);
        return true;
    }

    /**
     * jwt 认证
     *
     * @param response      响应
     * @param authorization 授权token
     * @return 是否放行
     * @throws Exception 异常
     */
    private boolean jwtAuth(HttpServletResponse response, String authorization) throws Exception {
        if (StringUtils.isBlank(authorization)) {
            log.error("用户未登陆,Authorization为空");
            returnNoLogin(response);
            return false;
        }
        String authToken = authorization.replaceAll("Bearer ", "");
        UserProfile userProfile = new UserProfile();
        try {
            DecodedJWT jwt = JwtUtils.verifyToken(authToken, JwtConstant.JWT_SECRET);
            Map<String, Claim> jwtClaims = jwt.getClaims();
            if (!jwtClaims.isEmpty()) {
                userProfile.setId(jwtClaims.get("id") == null ? null : Long.parseLong(jwtClaims.get("id").asString()));
                userProfile.setEmail(jwtClaims.get("email") == null ? null : jwtClaims.get("email").asString());
                userProfile.setUsername(jwtClaims.get("username") == null ? null : jwtClaims.get("username").asString());
                userProfile.setNickname(jwtClaims.get("nickname") == null ? null : jwtClaims.get("nickname").asString());
                userProfile.setRegTime(jwtClaims.get("reg_time") == null ? null : Long.parseLong(jwtClaims.get("reg_time").asString()));
                userProfile.setAvatar(jwtClaims.get("avatar") == null ? null : jwtClaims.get("avatar").asString());
                userProfile.setMobile(jwtClaims.get("mobile") == null ? null : jwtClaims.get("mobile").asString());
                userProfile.setSex(jwtClaims.get("sex") == null ? null : Integer.parseInt(jwtClaims.get("sex").asString()));
            } else {
                log.error("用户未登陆,token未解析到用户信息");
                returnNoLogin(response);
                return false;
            }
        } catch (Exception e) {
            log.error("用户未登陆,token解析异常：{}", e.getMessage(), e);
            returnNoLogin(response);
            return false;
        }
        // 存放用户线程对象
        LoginUserThread.put(userProfile);
        return true;
    }

}